package dm.api;

import dm.bsi.Deployment;
import dm.bsi.Image;
import dm.bsi.Log;
import dm.bsi.MSDeployment;
import dm.bsi.MSSDeployment;
import dm.bsi.Node;
import dm.bsi.Service;
import dm.bsi.WDeployment;
import dm.daemon.DeploymentDaemon;
import indi.zjh.dp.access.Api;
import indi.zjh.dp.access.ApiContext;
import indi.zjh.dp.db.Configuration;
import indi.zjh.dp.db.Session;
import indi.zjh.dp.orm.Query;
import indi.zjh.dp.orm.Result;
import indi.zjh.dp.type.GSet;
import indi.zjh.dp.verify.CharNotEmptyRule;
import indi.zjh.dp.verify.CharSetRule;
import indi.zjh.dp.verify.Check;
import indi.zjh.dp.verify.RegularRule;
import indi.zjh.dp.verify.StringSetRule;
import indi.zjh.dp.verify.ValueRangeRule;

public class AddDeploymentApi extends Api {

	@Override
	public void process(ApiContext ctx) throws Exception {
		// TODO Auto-generated method stub
		Session session = Configuration.getSessionFactory().createSession();
		try
		{
			String name = ctx.getRequestParamValue("name");
			String imageName = ctx.getRequestParamValue("image");
			
			String description = ctx.getRequestParamValue("desc");
			String hostPortStr = ctx.getRequestParamValue("hostPort");
			String containerPortStr = ctx.getRequestParamValue("containerPort");
			String modeStr = ctx.getRequestParamValue("mode");
			String replicasStr = ctx.getRequestParamValue("replicas");
			String protoStr = ctx.getRequestParamValue("proto");
			String catalogStr = ctx.getRequestParamValue("catalog");
			String secondHostPortStr = "0";
			Check.data(name).desc("请输入合法的服务名称name").rule(new CharSetRule(CharSetRule.NAME_SET));			
			Check.data(imageName).desc("请输入合法的镜像名称image").rule(new CharNotEmptyRule());
			Check.data(containerPortStr).desc("请输入应用的端口containerPort, 取值范围1-40000").rule(new ValueRangeRule(1, 40000));
			
			int catalog = Service.CATALOG_NORMAL;
			if(catalogStr == null || catalogStr.isEmpty())
			{
				catalogStr = "normal";
				Check.data(protoStr).desc("请输入正确的协议, http, tcp").rule(new StringSetRule(GSet.createStringSet().addString("http").addString("tcp")));
				Check.data(modeStr).desc("请输入合法的部署模式mode, w; ms; mss").rule(new StringSetRule(GSet.createStringSet().addString("w").addString("ms").addString("mss")));
			}
			else
			{
				Check.data(catalogStr).desc("请输入正确的服务类型, norml, redis, mysql").rule(new StringSetRule(GSet.createStringSet().addString("normal").addString("redis-sentinel").addString("redis-cluster").addString("mysql")));
				if(catalogStr.equals("redis-sentinel"))
				{
					catalog = Service.CATALOG_REDIS_SENTINEL;
					modeStr = "ms";
				}
				else if(catalogStr.equals("redis-cluster"))
				{
					catalog = Service.CATALOG_REDIS_CLUSTER;
					modeStr = "ms";
				}
				else if(catalogStr.equals("mysql"))
				{
					catalog = Service.CATELOG_MYSQL;
					modeStr = "ms";
				}
				protoStr = "tcp";
			}
			
			if(catalog == Service.CATELOG_MYSQL)
			{
				Check.data(hostPortStr).desc("请输入服务的端口hostPort和secondHostPort, 取值范围40000-40128, 中间用逗号隔开").rule(new CharSetRule("0123456789,"));
				String[] portparts = hostPortStr.split(",");
				if(portparts.length != 2)
				{
					throw new Exception("请输入服务的端口hostPort和secondHostPort, 取值范围40000-40128, 中间用逗号隔开");
				}
				
				Check.data(portparts[1]).desc("secondHostPort值非法").rule(new ValueRangeRule(40000, 40128));
				hostPortStr = portparts[0];
				secondHostPortStr = portparts[1];
			}
			
			Check.data(hostPortStr).desc("请输入服务的端口hostPort, 取值范围40000-40128").rule(new ValueRangeRule(40000, 40128));
			
			int secondHostPort = Integer.parseInt(secondHostPortStr);
			int hostPort = Integer.parseInt(hostPortStr);
			int containerPort = Integer.parseInt(containerPortStr);
			int proto = Service.PROTO_HTTP;
			if(protoStr.equals("tcp"))
				proto = Service.PROTO_TCP;
			else
				proto = Service.PROTO_HTTP;

				
			int workerCount = 0;
			int masterCount = 1;
			int slaveCount = 0;
			int sentinelCount = 0;
			int mode = Deployment.MODE_WORKER;
			if(modeStr.equals("ms"))
			{
				mode = Deployment.MODE_MASTER_SLAVE;
				Check.data(replicasStr).desc("请输入master容器和slave容器副本个数, 以逗号隔开").rule(new RegularRule("\\d+,\\d+"));
				String[] strs = replicasStr.split(",");
				Check.data(strs[0]).desc("副本个数, 取值范围1-64").rule(new ValueRangeRule(0, 64));
				Check.data(strs[1]).desc("副本个数, 取值范围0-64").rule(new ValueRangeRule(0, 64));
				masterCount = Integer.parseInt(strs[0]);
				slaveCount = Integer.parseInt(strs[1]);
			}
			else if(modeStr.equals("mss"))
			{
				mode = Deployment.MODE_MASTER_SLAVE_SENTINEL;
				Check.data(replicasStr).desc("请输入slave容器和sentinel容器副本个数, 以逗号隔开").rule(new RegularRule("\\d+,\\d+"));
				String[] strs = replicasStr.split(",");
				Check.data(strs[0]).desc("副本个数, 取值范围0-64").rule(new ValueRangeRule(0, 64));
				Check.data(strs[1]).desc("副本个数, 取值范围0-64").rule(new ValueRangeRule(0, 64));
				slaveCount = Integer.parseInt(strs[0]);
				sentinelCount = Integer.parseInt(strs[1]);
			}
			else 
			{
				mode = Deployment.MODE_WORKER;
				Check.data(replicasStr).desc("请输入容器副本个数replicas, 取值范围0-64").rule(new ValueRangeRule(0, 64));
				workerCount = Integer.parseInt(replicasStr);
			}
			
			
			// 检查部署名称是否存在
			Query<Deployment> query = new Query<Deployment>(session);
			Result<Long> result = query.find(Deployment.class, "`name` = '"+name+"'").idResult();
			if(result.count() > 0)
			{
				throw new Exception("部署名称["+name+"]已经存在");
			}
			
			// 检查端口是否被占用
			String checkcond = "";
			if(secondHostPort > 0)
				checkcond = " OR `secondHostPort` = "+secondHostPort;
			Result<Long> portResult = query.find(Deployment.class, "`hostPort` = "+hostPort+checkcond).idResult();
			if(portResult.count() > 0)
			{
				throw new Exception("服务端口["+hostPort+"]已经被使用");
			}
			
			
			
			Image image = Image.getImageByName(session, imageName);
			if(image == null)
			{
				image = new Image();
				image.setName(imageName);
				session.save(image);
			}
			
			Deployment deployment = null;
			if(mode == Deployment.MODE_WORKER)
			{
				WDeployment tmp = new WDeployment();
				tmp.setExpectedWokerCount(workerCount);
				deployment = tmp;
			}
			else if(mode == Deployment.MODE_MASTER_SLAVE)
			{
				MSDeployment tmp = new MSDeployment();
				tmp.setExpectedMasterCount(masterCount);
				tmp.setExpectedSlaveCount(slaveCount);
				deployment = tmp;
			}
			else
			{
				MSSDeployment tmp = new MSSDeployment();
				tmp.setExpectedMasterCount(1);
				tmp.setExpectedSlaveCount(slaveCount);
				tmp.setExpectedSentinelCount(sentinelCount);
				deployment = tmp;
			}
			
			deployment.setContainerPort(containerPort);
			deployment.setCurContainerPort(containerPort);
			if(description.isEmpty())
				deployment.setDescription(name);
			else
				deployment.setDescription(description);
			deployment.getImage().add(image);
			deployment.setHostPort(hostPort);
			deployment.setSecondHostPort(secondHostPort);
			deployment.setMode(mode);
			deployment.setName(name);
			session.save(deployment);
			
			Service service = new Service();
			service.setName(name);
			service.setPort(hostPort);
			service.setSecondPort(secondHostPort);
			service.setProto(proto);
			service.setCatalog(catalog);
			session.save(service);
			
			deployment.getService().add(service);
			service.getDeployment().add(deployment);
			session.save(deployment).save(service);
			
			Log.success(session, deployment, "添加部署["+name+"]"+deployment+service);
			
			session.commit();
			
			DeploymentDaemon daemon = new DeploymentDaemon(deployment);
			new Thread(daemon).start();
			
			ctx.writeResponse("OK");
			
		}
		catch(Exception e)
		{
			ctx.writeResponse("FAIL:"+e.getMessage());
			session.rollback();
			e.printStackTrace();
		}
		finally {
			session.close();
		}
	}

}
